import java.util.*;

public class FileManage {
    static String userName = "Administrator";
    private static final Directory root = new Directory("root", null, "Administrator", "faculty");
    static Directory currentDirectory = root;


    static void initiate() {// initiate some files and directories
        Directory d1 = new Directory("dir1", root, userName, "faculty");
        Directory d2 = new Directory("dir2", root, userName, "faculty");
        Directory d1_childDir = new Directory("d1_childDir", d1, userName, "faculty");
        Directory d2_childDir = new Directory("d2_childDir", d2, userName, "faculty");
        File d1_childFile = new File("d1_childFile", userName, "staff");
        File d2_childFile = new File("d2_childFile", userName, "staff");
        root.subDirList.add(d1);
        root.subDirList.add(d2);
        d1_childFile.setSize(2048);
        d2_childFile.setSize(512);
        d1.fileList.add(d1_childFile);
        d2.fileList.add(d2_childFile);
        d1.subDirList.add(d1_childDir);
        d2.subDirList.add(d2_childDir);
        d1.setSize(d1_childDir.getSize()+d1_childFile.getSize());
        d2.setSize(d2_childDir.getSize()+ d2_childFile.getSize());
        root.setSize(d1.getSize()+ d2.getSize());
    }

    static void commands() {
        initiate();
        while (true) {
            System.out.print(dirTrace(currentDirectory) + " @" + userName + " -> ");
            Scanner scanner = new Scanner(System.in);
            String command = scanner.nextLine();
            // remove prefix space to get the command
            command = removePrefixSpace(command);

            try {
                // commandInfo is a String array representing the set of command and subsequent
                // input
                String[] commandInfo = Objects.requireNonNull(command).split(" ");

                switch (commandInfo[0]) {
                    case "pwd":
                        pwd();
                        break;
                    case "ls":
                        // check the command length to confirm whether it is "ls -l" or "ls"
                        lsOperation(commandInfo.length == 2 ? commandInfo[1] : commandInfo[0]);
                        break;
                    case "cd":
                        changeDirectory(commandInfo[1]);
                        break;
                    case "mk":
                        if (commandInfo.length == 1) {
                            System.out.println("file name can't be null");
                        } else {
                            createFile(commandInfo[1],currentDirectory);
                        }
                        break;
                    case "mkdir":
                        if (commandInfo.length == 1) {
                            System.out.println("directory name can't be null");
                        } else {
                            createDirectory(commandInfo[1],currentDirectory);
                        }
                        break;
                    case "rm":
                        removeFile(commandInfo[1]);
                        break;
                    case "rmdir":
                        removeDirectory(commandInfo[1]);
                        break;
                    case "chmod":
                        changeMode(commandInfo[1], commandInfo[2]);
                        break;
                    case "exit":

                    case "quit":
                        Thread.sleep(1000);
                        System.out.println("QUIT COMPLETED");
                        System.exit(0);
                    default:
                        System.out.println("Command Not Supported.Please check the format >~<");
                }
            } catch (NullPointerException e) {
                System.out.println("Command -> \"" + command + "\" is not available");
            } catch (IllegalArgumentException e) {
                System.out.println("\"" + e.getMessage() + "\" already exists");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }

    private static void pwd() {
        System.out.println(dirTrace(currentDirectory));
    }

    // show detail of files and sub directories
    private static void showFileDetailed(File file) {
        System.out.println(file.detail());
    }

    private static void showDirDetailed(Directory dir) {
        System.out.println(dir.detail());
    }

    private static String dirTrace(Directory directory) {// trace the directory path
        return directory.getParent() == null ? // use recursive to prepend parent directory in turn
                root.getName() : dirTrace(Objects.requireNonNull(directory.getParent())) + "/" + directory.getName();
    }

    private static void lsOperation(String command) {
        switch (command) {
            case "-l":// print the data in long format
                currentDirectory.fileList.forEach(FileManage::showFileDetailed);
                currentDirectory.subDirList.forEach(FileManage::showDirDetailed);
                break;
            case "ls": // just name
                displayFiles(currentDirectory);
                displayDir(currentDirectory);
                break;
            default:
                throw new NullPointerException();
        }

    }

    private static void displayFiles(Directory directory) {
        directory.fileList.forEach(System.out::println);
    }

    private static void displayDir(Directory directory) {
        directory.subDirList.forEach(System.out::println);
    }

    private static void changeDirectory(String location) {// cd
        if (location.charAt(0) == '/') currentDirectory = root; // "/" means root
        String[] args = location.split("/");
        for (String arg : args) {
           if (arg.equals("..")) { // "../" means parent's parent
                try {
                    assert currentDirectory != null;
                    currentDirectory = currentDirectory.getParent();
                } catch (NullPointerException exception) {
                    System.out.println("can not go up");
                }
            }else if(arg.equals(".")){
            // stay in the current directory
           } else {
                if (arg.equals("")) {
                    break;// do nothing
                } else {
                    // goto directory whose name is arg
                    assert currentDirectory != null;
                    if (findDir(currentDirectory, arg) == null) {
                        System.out.println("no such directory");
                    } else {
                        currentDirectory = findDir(currentDirectory, arg);
                    }
                }
            }
        }
    }

    private static void createFile(String name, Directory directory) {
        if (name != null) {
            checkRedundant(directory, name); // no redundant file
            //add the new file to the file list
            File newFile = new File(name, userName, "staff");
            directory.fileList.add(newFile);
            directory.setSize(directory.getSize()+newFile.getSize());//change the directory size
        } else {
            System.out.println("file name can't be null");
        }
    }

    private static void checkRedundant(Directory dir, String name) {
        if (dir.subDirList.stream().anyMatch(s -> s.getName().equals(name))
                || dir.fileList.stream().anyMatch(s -> s.getName().equals(name)))
            throw new IllegalArgumentException(name);
    }

    private static void createDirectory(String name, Directory directory) {
        if (name != null) {
            checkRedundant(directory, name);// no redundant directory
            Directory newDir = new Directory(name, directory, userName, "staff");
            directory.subDirList.add(newDir);
            directory.setSize(directory.getSize()+newDir.getSize());
        } else {
            System.out.println("directory name can't be null");
        }

    }

    private static void removeFile(String name) {
        if (findFile(currentDirectory, name) == null) {
            System.out.println("No such file");
        } else {
            // remove the file from the file list
            currentDirectory.fileList.remove(findFile(currentDirectory, name));
        }
    }

    private static void removeDirectory(String name) {
        if (findDir(currentDirectory, name) == null) {
            System.out.println("No such directory");
        } else {
            // remove the file from the subDirlist
            currentDirectory.subDirList.remove(findDir(currentDirectory, name));
        }

    }

    private static void changeMode(String mode, String name) {
        int[] modeIntArr = new int[3];
        try {
            //transfer int mode into array
            int modeDec = Integer.parseInt(mode);
            for (int i = 0; i < 3; i++) {
                int curBit = modeDec > 10 ? modeDec % 10 : modeDec;
                if (curBit < 1 || curBit > 7) {
                    throw new NumberFormatException();
                } else {
                    modeIntArr[2 - i] = curBit;
                }
                modeDec /= 10;
            }
        } catch (NumberFormatException e) {
            System.out.println("Mode Input error!!");
        }
        // set the new mode
        File curFile = findFile(currentDirectory, name);
        Directory curDir = findDir(currentDirectory, name);
        Mode realMode = new Mode(modeIntArr);
        if (curFile != null) {
            curFile.setMode(realMode);
        } else if (curDir != null) {
            curDir.setMode(realMode);
        } else {
            System.out.println("Error: There is no such " + name + "in current directory");
        }
    }

    private static File findFile(Directory dir, String name) {
        try {
            return dir.fileList.stream().filter(s -> s.getName().equals(name)).findAny().orElseThrow();
        } catch (NoSuchElementException e) {
            return null;
        }
    }

    private static Directory findDir(Directory dir, String name) {
        try {
            return dir.subDirList.stream().filter(s -> s.getName().equals(name)).findAny().orElseThrow();
        } catch (NoSuchElementException e) {
            return null;
        }
    }

    private static String removePrefixSpace(String str) {
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] != ' ')
                return String.valueOf(Arrays.copyOfRange(chars, i, chars.length));
        }
        return null;
    }

}